package util;

import apisdk.dto.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 * <p>
 * Title: Module Information
 * </p>
 * <p>
 * Description: Function Description
 * </p>
 * <p>
 * Copyright: Copyright (c) 2015
 * </p>
 * <p>
 * Create Time: 2016/7/6
 * </p>
 *
 * @author YangYang
 *         <p>
 *         Update Time:
 *         </p>
 *         <p>
 *         Updater:
 *         </p>
 *         <p>
 *         Update Comments:
 *         </p>
 */

public class ModelClassGenerator {
    public static final String VO_PACKAGE_NAME = "vo";
    public static final String REPOSITORY_PACKAGE_NAME = "repository";
    public static final String CONTROLLER_PACKAGE_NAME = "controller";
    public static final String SERVICE_PACKAGE_NAME = "service";

    private List<String> modelClasses;

    private List<String> packageList;

    private String basePackagePath;

    private String basePackage;

    private String versionPath = "/";
    private static final Configuration CFG = new Configuration(Configuration.VERSION_2_3_23);

    static {
        CFG.setClassLoaderForTemplateLoading(ModelClassGenerator.class.getClassLoader(), "/template/");
        CFG.setDefaultEncoding("UTF-8");
        CFG.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
        CFG.setLogTemplateExceptions(false);
    }

    public void doGenerateClass() throws Exception {
        initFolder();
        if (modelClasses == null) {
            modelClasses = new ArrayList<>();
        }

        for (String packageName : packageList) {
            modelClasses.addAll(SdkClassUtil.getClassName(packageName));
        }
        modelClasses = new ArrayList<>(new HashSet<>(modelClasses));

        for (String className : modelClasses) {
            processClass(className);
        }
    }

    private void initFolder() {
        String root = ClassLoader.getSystemResource("").getPath().toString().substring(1).replace("target/classes/", "") + "src/main/java/";
        basePackagePath = basePackage.replace(".", "/");
        if (!basePackagePath.endsWith("/")) {
            basePackagePath = basePackagePath + "/";
        }
        basePackagePath = root + basePackagePath;

        File controller = new File(basePackagePath + CONTROLLER_PACKAGE_NAME + "/");
        controller.mkdirs();

        File repository = new File(basePackagePath + REPOSITORY_PACKAGE_NAME + "/");
        repository.mkdirs();

    }

    public void processClass(String className) throws Exception {
        System.out.println("--------------------" + className + "----------------------------");

        generateRepository(className);
        generateVO(className);
        generateService(className);
        generateController(className);

    }

    private void generateService(String className) throws ClassNotFoundException, IOException, TemplateException {
        Class clz = Class.forName(className);
        String modelName = clz.getSimpleName();

        String simpleName = clz.getSimpleName();
        String controllerClassName = simpleName + "Service";
        Template template = CFG.getTemplate("ServiceTemplate.ftl");
        Writer out = new StringWriter();
        SdkController sdkWafController = new SdkController();
        sdkWafController.setClassName(controllerClassName);
        sdkWafController.setPackageName(basePackage + "." + SERVICE_PACKAGE_NAME);
        sdkWafController.setBasePath(versionPath + SdkStringUtil.toLowerCaseFirstOne(simpleName));
        sdkWafController.setModelName(simpleName);

        List<String> importPackage = new ArrayList<>();
        importPackage.add(clz.getName());
        importPackage.add(basePackage + "." + VO_PACKAGE_NAME + "." + modelName + "Vo");
        importPackage.add(basePackage + "." + REPOSITORY_PACKAGE_NAME + "." + modelName + "Repository");
        sdkWafController.setImportPackage(importPackage);

        List<String> service = new ArrayList<>();
        service.add(simpleName + "Repository");
        sdkWafController.setServices(service);
        template.process(sdkWafController, out);
        FileUtils.write(new File(basePackagePath + SERVICE_PACKAGE_NAME + "/" + controllerClassName + ".java"), out.toString());
        System.out.println(out.toString());
    }

    private void generateController(String className) throws ClassNotFoundException, IOException, TemplateException {
        Class clz = Class.forName(className);
        String modelName = clz.getSimpleName();

        String simpleName = clz.getSimpleName();
        String controllerClassName = simpleName + "Controller";
        Template template = CFG.getTemplate("ControllerTemplate.ftl");
        Writer out = new StringWriter();
        SdkController sdkWafController = new SdkController();
        sdkWafController.setClassName(controllerClassName);
        sdkWafController.setPackageName(basePackage + "." + CONTROLLER_PACKAGE_NAME);
        sdkWafController.setBasePath(versionPath + SdkStringUtil.toLowerCaseFirstOne(simpleName));
        sdkWafController.setModelName(simpleName);

        List<String> importPackage = new ArrayList<>();
        importPackage.add(clz.getName());
        importPackage.add(basePackage + "." + VO_PACKAGE_NAME + "." + modelName + "Vo");
        importPackage.add(basePackage + "." + REPOSITORY_PACKAGE_NAME + "." + modelName + "Repository");
        importPackage.add(basePackage + "." + SERVICE_PACKAGE_NAME + "." + modelName + "Service");
        sdkWafController.setImportPackage(importPackage);

        List<String> service = new ArrayList<>();
        service.add(simpleName + "Repository");
        sdkWafController.setServices(service);
        template.process(sdkWafController, out);
        FileUtils.write(new File(basePackagePath + CONTROLLER_PACKAGE_NAME + "/" + controllerClassName + ".java"), out.toString());
        System.out.println(out.toString());
    }

    private void generateRepository(String className) throws ClassNotFoundException, IOException, TemplateException {
        Class clz = Class.forName(className);
        String modelName = clz.getSimpleName();
        String repositoryClassName = modelName + "Repository";
        SdkRepository sdkRepository = new SdkRepository();
        sdkRepository.setModelClassName(modelName);
        sdkRepository.setPackageName(basePackage + "." + REPOSITORY_PACKAGE_NAME);
        sdkRepository.setClassName(repositoryClassName);

        List<String> importPackage = new ArrayList<>();
        importPackage.add(clz.getName());
        sdkRepository.setImportPackage(importPackage);

        List<SdkFields> fields = new ArrayList<>();
        for (Field field : clz.getDeclaredFields()) {
            String type = field.getType().getName();
            String name = field.getName();
            SdkFields sdkRepositoryFields = new SdkFields();
            sdkRepositoryFields.setName(name);
            sdkRepositoryFields.setType(type);
            fields.add(sdkRepositoryFields);
        }

        sdkRepository.setQueryFields(fields);

        Template template = CFG.getTemplate("RepositoryTemplate.ftl");
        Writer out = new StringWriter();
        template.process(sdkRepository, out);
        FileUtils.write(new File(basePackagePath + REPOSITORY_PACKAGE_NAME + "/" + repositoryClassName + ".java"), out.toString());
        System.out.println(out.toString());
    }

    private void generateVO(String className) throws ClassNotFoundException, IOException, TemplateException {
        Class clz = Class.forName(className);
        String modelName = clz.getSimpleName();
        String voClassName = modelName + "Vo";
        SdkVO sdkVO = new SdkVO();
        sdkVO.setModelClassName(modelName);
        sdkVO.setPackageName(basePackage + "." + VO_PACKAGE_NAME);
        sdkVO.setClassName(voClassName);

        List<String> importPackage = new ArrayList<>();
        importPackage.add(clz.getName());
        sdkVO.setImportPackage(importPackage);

        List<SdkVoFields> fields = new ArrayList<>();
        for (Field field : clz.getDeclaredFields()) {
            String type = field.getType().getName();
            String name = field.getName();
            SdkVoFields sdkVoFields = new SdkVoFields();
            sdkVoFields.setName(name);
            sdkVoFields.setType(type);
            sdkVoFields.setJsonFiled(SdkStringUtil.toUnderLineWithLowerCaseInStr(name));
            fields.add(sdkVoFields);
        }

        sdkVO.setFields(fields);

        Template template = CFG.getTemplate("VoTemplate.ftl");
        Writer out = new StringWriter();
        template.process(sdkVO, out);
        FileUtils.write(new File(basePackagePath + VO_PACKAGE_NAME + "/" + voClassName + ".java"), out.toString());
    }

    public void addPackage(String packageName) {
        if (packageList == null) {
            packageList = new ArrayList<String>();
        }
        packageList.add(packageName);
    }

    public void addClass(String className) {
        if (modelClasses == null) {
            modelClasses = new ArrayList<>();
        }
        modelClasses.add(className);
        modelClasses = new ArrayList<>(new HashSet<>(modelClasses));
    }

    public String getBasePackagePath() {
        return basePackagePath;
    }

    public void setBasePackagePath(String basePackagePath) {
        this.basePackagePath = basePackagePath;
    }

    public String getBasePackage() {
        return basePackage;
    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public static void main(String[] args) throws Exception {
        ModelClassGenerator modelClassGenerator = new ModelClassGenerator();
        modelClassGenerator.setBasePackage("com.nd.test");
        modelClassGenerator.addPackage("model.po");
        modelClassGenerator.doGenerateClass();
        //modelClassGenerator.processClass();
    }
}
